//! 数组和切片示例
//!
//! 数组是固定大小的存储相同类型元素的数据结构
//!
//! ## 知识点总结
//!
//! | 知识点 | 说明 |
//! |--------|------|
//! | 数组声明 | 使用`[T; N]`语法声明包含N个T类型元素的数组 |
//! | 数组初始化 | 可以使用`[val; N]`语法将N个元素都初始化为val |
//! | 数组访问 | 通过索引访问数组元素，如`a[0]` |
//! | 切片 | 数组的引用，如`&a[1..3]` |
//! | 越界检查 | 运行时检查数组访问越界，越界会panic |

/// 数组示例
///
/// 数组具有固定的长度，一旦声明就不能改变大小
/// 对于每个元素都相同的情况，还可以通过指定初始值、后跟分号和方括号中的数组长度来初始化数组
pub fn array_demo() {
    // 声明一个包含5个i32元素的数组
    let a = [1, 2, 3, 4, 5];

    // 声明一个包含5个i32元素的数组，所有元素初始化为0
    let b: [i32; 5] = [0; 5];

    // 声明一个指定类型的数组
    let c: [i32; 3] = [1, 2, 3];

    println!("数组示例:");
    println!("数组 a: {a:?}");
    println!("数组 b: {b:?}");
    println!("数组 c: {c:?}");

    // 访问数组元素
    let first = a[0];
    let second = a[1];

    println!("第一个元素: {first}");
    println!("第二个元素: {second}");

    // 对于每个元素都相同的情况，还可以通过指定初始值、后跟分号和方括号中的数组长度来初始化数组
    let a = [3; 5];
    assert!(a[3] == 3);

    // 数组元素求和
    let a = [1, 2, 3, 4, 5];
    let iter = a.iter();
    let total: i32 = iter.sum();
    assert_eq!(total, 15);
    println!("数组元素总和: {total}");
}

/// 数组访问示例
///
/// 通过索引访问数组元素，索引从0开始
pub fn array_access() {
    let a = [1, 2, 3, 4, 5];

    // 访问数组元素
    println!("数组的第一个元素是: {}", a[0]);
    println!("数组的第三个元素是: {}", a[2]);

    // 遍历数组
    println!("遍历数组:");
    for element in a.iter() {
        println!("{element}");
    }

    // 使用foreach方式遍历数组
    println!("使用foreach方式遍历数组:");
    a.iter().for_each(|x| println!("{x}"));
}

/// 切片示例
///
/// 切片允许引用数组的连续部分
///
/// 字符串切片
pub fn slice_demo() {
    let a = [1, 2, 3, 4, 5];

    // 创建整个数组的切片
    let slice = &a[..];
    println!("整个数组的切片: {slice:?}");

    // 创建数组部分元素的切片
    let slice = &a[1..3];
    println!("部分元素的切片: {slice:?}");

    // 从开始到指定索引的切片
    let slice = &a[..3];
    println!("从开始到索引3的切片: {slice:?}");

    // 从指定索引到结束的切片
    let slice = &a[2..];
    println!("从索引2到结束的切片: {slice:?}");

    // 字符串切片示例
    let s = String::from("hello world");
    let hello = &s[0..5];
    let world = &s[6..11];
    println!("字符串切片: {hello}, {world}");

    // 数组切片示例
    let a = [1, 2, 3, 4, 5];
    let slice = &a[1..3];
    assert_eq!(slice, &[2, 3]);
    println!("数组切片: {slice:?}");

    // 向量切片示例
    // 向量（Vec）在 Rust 中可以被理解为动态大小的可变数组，但它与固定大小的数组有重要区别
    let vec = vec![10, 20, 30, 40];
    let slice = &vec[0..2]; // 类型: &[i32]，内容 [10, 20]
    assert_eq!(slice, &[10, 20]);
    println!("向量切片: {slice:?}");
    // &\[10, 20\] 的本质
    // 匿名数组：\[10, 20\] 是一个在栈上分配的临时数组，类型是 \[i32; 2\]
    // 隐式转换：& 操作符将这个数组自动转换为切片 &\[i32\]
    // 生命周期：这个临时数组的生命周期仅限于当前表达式
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_array_demo() {
        array_demo();
    }

    #[test]
    fn test_array_access() {
        array_access();
    }

    #[test]
    fn test_slice_demo() {
        slice_demo();
    }
}
